package cn.geminis.crypto.csp;

import cn.geminis.crypto.core.key.KeyPair;
import cn.geminis.crypto.core.key.PublicKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.io.SignerOutputStream;
import org.bouncycastle.operator.ContentSigner;

import java.io.Closeable;
import java.io.OutputStream;
import java.util.Objects;

/**
 * @author Allen
 */
public abstract class AbstractSigner implements org.bouncycastle.crypto.Signer, Closeable {

    protected PublicKey verifyPublicKey;
    protected KeyPair keyPair;

    /**
     * 获取算法OID
     *
     * @return 算法OID
     */
    public abstract String getAlgOid();

    /**
     * 获取摘要算法OID
     *
     * @return 算法OID
     */
    public abstract String getDigestAlgOid();

    /**
     * 获取密钥对，如果私钥受保护，则只返回公钥
     *
     * @return 密钥对
     */
    public abstract KeyPair getKeyPair();

    /**
     * 设置已计算的摘要值
     *
     * @param data 已计算的摘要值
     */
    public abstract void setState(byte[] data);

    /**
     * 获取已计算的摘要值
     *
     * @return 已计算的摘要值
     */
    public abstract byte[] getState();

    public PublicKey getPublicKey() {
        if (Objects.isNull(this.keyPair)) {
            this.keyPair = getKeyPair();
        }
        return this.keyPair.getPublicKey();
    }

    @Override
    public void update(byte b) {
        update(new byte[]{b}, 0, 1);
    }

    public void setKeyPair(KeyPair keyPair) {
        this.keyPair = keyPair;
    }

    public byte[] generateSignature(byte[] data) {
        this.reset();
        this.init(true, null);
        this.update(data, 0, data.length);
        try {
            return this.generateSignature();
        } catch (CryptoException e) {
            throw new RuntimeException("计算签名错误", e);
        }
    }

    public boolean verifySignature(byte[] data, byte[] signature, PublicKey publicKey) {
        this.verifyPublicKey = publicKey;

        this.reset();
        this.init(false, null);
        this.update(data, 0, data.length);
        return this.verifySignature(signature);
    }

    public ContentSigner createContentSigner() {
        return new CryptoContentSigner(this);
    }

    public static class CryptoContentSigner implements ContentSigner {
        private AbstractSigner signer;

        CryptoContentSigner(AbstractSigner signer) {
            this.signer = signer;
        }

        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return new AlgorithmIdentifier(new ASN1ObjectIdentifier(signer.getAlgOid()));
        }

        @Override
        public OutputStream getOutputStream() {
            return new SignerOutputStream(signer);
        }

        @Override
        public byte[] getSignature() {
            try {
                return signer.generateSignature();
            } catch (Exception e) {
                throw new RuntimeException("计算签名值错误", e);
            }
        }
    }

}
